/*
 * SingleThreadRequestProcesser.java
 *
 * Created on 2008/06/08 +0900
 *
 * $Revision: 38 $
 * $Date: 2008-10-11 08:48:45 +0000 (Sat, 11 Oct 2008) $
 */

package org.gikolet.framework.request;

import org.gikolet.framework.util.BlockingQueue;
import org.gikolet.framework.util.Deque;
import org.gikolet.framework.util.Stack;

/**
 * @author Tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 38 $ $Date: 2008-10-11 08:48:45 +0000 (Sat, 11 Oct 2008) $
 */
public class SingleThreadRequestProcesser implements RequestProcesser {
    private final BlockingQueue queue;
    private final Stack threadStack;

    public SingleThreadRequestProcesser() {
	this.queue = new BlockingQueue();
	this.threadStack = new Deque();
    }

    public void invoke(Request request) {
	this.queue.pushQueue(request);
    }

    public void invokeAndWait(final Request request) throws InterruptedException,
	    Throwable {
	if (isRequestProcesserThread()) {
	    throw new RequestException("Current thread is request thread.");
	}
	ProxyRequest proxyRequest = new ProxyRequest(request);

	this.queue.blockingPushQueue(proxyRequest);

	Throwable throwable = proxyRequest.getExecuteThrowable();
	if (throwable != null) {
	    throw throwable;
	}
    }

    // public RequestProcesser createRequestProcesser() {
    // return null;
    // }

    public synchronized boolean isRequestProcesserThread() {
	return !this.threadStack.isEmpty()
		&& this.threadStack.peekStack() == Thread.currentThread();
    }

    class RequestThread extends Thread {
	public void run() {
	    while (isRequestProcesserThread()) {
		try {
		    Request request = (Request) SingleThreadRequestProcesser.this.queue
			    .blockingPopQueue();
		    request.execute();
		} catch (InterruptedException e) {
		} catch (Exception e) {
		    e.printStackTrace();
		}
	    }
	}
    }

    public void start() {
	Thread newRequestThread;

	boolean requestProcesserThread = isRequestProcesserThread();

	synchronized (this) {
	    newRequestThread = new RequestThread();
	    this.threadStack.pushStack(newRequestThread);
	    newRequestThread.start();
	}

	if (requestProcesserThread) {
	    while (newRequestThread.isAlive()) {
		try {
		    newRequestThread.join();
		} catch (InterruptedException e) {
		}
	    }
	}
    }

    public synchronized void stop() {
	if (isAlive()) {
	    Thread thread = (Thread) this.threadStack.popStack();
	    thread.interrupt();
	}
    }

    public synchronized void stopAll() {
	while (isAlive()) {
	    stop();
	}
    }

    public synchronized boolean isAlive() {
	return !this.threadStack.isEmpty();
    }
}
